1 /*
2  * Hunt - a framework for web and console application based on Collie using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.application.config;
13 
14 import std.exception;
15 import std.parallelism : totalCPUs;
16 import std.socket : Address, parseAddress;
17 import kiss.logger;
18 import std.string;
19 
20 import hunt.init;
21 import hunt.text.configuration;
22 import hunt.application.application : WebSocketFactory;
23 import hunt.application.application;
24 
25 import collie.codec.http.server.httpserveroptions;
26 
27 final class AppConfig
28 {
29     alias AddressList = Address[];
30 
31     struct ApplicationConf
32     {
33         string name = "HUNT APPLICATION";
34         string baseUrl;
35         string defaultCookieDomain = ".example.com";
36         string defaultLanguage = "zh-CN";
37         string languages = "zh-CN,en-US";
38         string secret = "CD6CABB1123C86EDAD9";
39         string encoding = "utf-8";
40         int staticFileCacheMinutes = 30;
41     }
42 
43     struct SessionConf
44     {
45 		string storage = "memory";
46         string prefix = "huntsession_";
47 		string args = "/tmp";
48         uint expire = 3600;
49     }
50 
51     struct CacheConf
52     {
53 		string storage = "memory";
54 		string args = "/tmp";
55 		bool   enableL2 = false;	
56     }
57 
58     struct HttpConf
59     {
60         string address = "0.0.0.0";
61         ushort port = 8080;
62         uint workerThreads = 4;
63         uint ioThreads = 2;
64         size_t keepAliveTimeOut = 30;
65         size_t maxHeaderSize = 60 * 1024;
66         int cacheControl;
67         string path;
68     }
69 
70     struct HttpsConf
71     {
72         bool enabled = false;
73         string protocol;
74         string keyStore;
75         string keyStoreType;
76         string keyStorePassword;
77     }
78 
79     struct RouteConf
80     {
81         string groups;
82     }
83 
84     struct LogConfig
85     {
86         string level = "all";
87         string path;
88         string file = "";
89 		bool   disableConsole = false;
90 		string maxSize = "8M";
91 		uint   maxNum = 8;
92     }
93 
94     struct MemcacheConf
95     {
96         bool enabled = false;
97         string servers;
98     }
99 
100     struct RedisConf
101     {
102         bool enabled = false;
103         string host = "127.0.0.1";
104         string password = "";
105         ushort database = 0;
106         ushort port = 6379;
107         uint timeout = 0;
108     }
109 
110     struct UploadConf
111     {
112         string path;
113         uint maxSize = 4 * 1024 * 1024;
114     }
115 
116     struct MailSmtpConf
117     {
118         string host;
119         string channel;
120         ushort port;
121         string protocol;
122         string user;
123         string password;
124     }
125     
126     struct MailConf
127     {
128         MailSmtpConf smtp;
129     }
130 
131     struct DbPoolConf
132     {
133         uint maxConnection = 10;
134         uint minConnection = 10;
135         uint timeout = 10000;
136     }
137 
138     struct DBConfig
139     {
140         string url;
141         DbPoolConf pool;
142     }
143 
144     struct DateConf
145     {
146         string format;
147         string timeZone;
148     }
149 
150     struct CornConf
151     {
152         string noon;
153     }
154 
155     struct ServiceConf {
156         string address = "127.0.0.1";
157         ushort port;
158         int workerThreads;
159         string password;
160     }
161 
162     struct RpcConf {
163         bool enabled = true;
164         ServiceConf service;
165     }
166 
167     struct Templates
168     {
169         string path;
170     }
171 
172     DBConfig database;
173     ApplicationConf application;
174     SessionConf session;
175     CacheConf cache;
176     HttpConf http;
177     HttpsConf https;
178     RouteConf route;
179     MemcacheConf memcache;
180     RedisConf redis;
181     LogConfig log;
182     UploadConf upload;
183     CornConf cron;
184     DateConf date;
185     MailConf mail;
186     RpcConf rpc;
187     Templates templates;
188     
189     @property Configuration config(){return _config;}
190 
191     static AppConfig parseAppConfig(Configuration conf)
192     {
193         AppConfig app = new AppConfig();
194 
195         app._config = conf;
196         
197         collectException(conf.application.name.value,    app.application.name);
198         collectException(conf.application.baseUrl.value,    app.application.baseUrl);
199         collectException(conf.application.defaultCookieDomain.value,    app.application.defaultCookieDomain);
200         collectException(conf.application.defaultLanguage.value,    app.application.defaultLanguage);
201         collectException(conf.application.languages.value,    app.application.languages);
202         collectException(conf.application.secret.value,    app.application.secret);
203         collectException(conf.application.encoding.value,    app.application.encoding);
204         collectException(conf.application.staticFileCacheMinutes.as!int,    app.application.staticFileCacheMinutes);
205 
206 		collectException(conf.session.storage.value(),    app.session.storage);
207         collectException(conf.session.prefix.value(),    app.session.prefix);
208 		collectException(conf.session.args.value(),    app.session.args);
209         collectException(conf.session.expire.as!uint(),     app.session.expire);
210 
211 		collectException(conf.cache.storage.value(),    app.cache.storage);
212         ///collectException(conf.cache.prefix.value(),    app.cache.prefix);
213 		collectException(conf.cache.args.value(),     app.cache.args);
214 
215         collectException(conf.http.address.value(), app.http.address);
216         collectException(conf.http.port.as!ushort(), app.http.port);
217         collectException(conf.http.workerThreads.as!uint(), app.http.workerThreads);
218         collectException(conf.http.ioThreads.as!uint(), app.http.ioThreads);
219         collectException(conf.http.maxHeaderSize.as!size_t(), app.http.maxHeaderSize);
220         collectException(conf.http.keepAliveTimeOut.as!size_t(), app.http.keepAliveTimeOut);
221         collectException(conf.http.cacheControl.as!int(), app.http.cacheControl);
222         collectException(conf.http.path.value(), app.http.path);
223 
224         collectException(conf.https.enabled.as!bool(), app.https.enabled);
225         collectException(conf.https.protocol.value(), app.https.protocol);
226         collectException(conf.https.keyStore.value(), app.https.keyStore);
227         collectException(conf.https.keyStoreType.value(), app.https.keyStoreType);
228 
229         collectException(conf.route.groups.value(), app.route.groups);
230 
231         collectException(conf.memcache.enabled.as!bool(), app.memcache.enabled);
232         collectException(conf.memcache.servers.value(), app.memcache.servers);
233 
234         collectException(conf.redis.enabled.as!bool(), app.redis.enabled);
235         collectException(conf.redis.host.value(), app.redis.host);
236         collectException(conf.redis.password.value(), app.redis.password);
237         collectException(conf.redis.database.as!ushort(), app.redis.database);
238         collectException(conf.redis.port.as!ushort(), app.redis.port);
239         collectException(conf.redis.timeout.as!uint(), app.redis.timeout);
240 
241         collectException(conf.log.level.value(), app.log.level);
242         collectException(conf.log.path.value(), app.log.path);
243         collectException(conf.log.file.value(), app.log.file);
244 		collectException(conf.log.disableConsole.as!bool(), app.log.disableConsole);
245 		collectException(conf.log.maxSize.value(), app.log.maxSize);
246 		collectException(conf.log.maxNum.as!uint(), app.log.maxNum);
247 
248         collectException(conf.upload.path.value(), app.upload.path);
249         collectException(conf.upload.maxSize.as!uint(), app.upload.maxSize);
250 
251         collectException(conf.cron.noon.value(), app.cron.noon);
252 
253         collectException(conf.date.format.value(), app.date.format);
254         collectException(conf.date.timeZone.value(), app.date.timeZone);
255 
256         collectException(conf.database.url.value(), app.database.url);
257         collectException(conf.database.pool.maxConnection.as!uint(), app.database.pool.maxConnection);
258         collectException(conf.database.pool.minConnection.as!uint(), app.database.pool.minConnection);
259         collectException(conf.database.pool.timeout.as!uint(), app.database.pool.timeout);
260 
261         collectException(conf.mail.smtp.host.value(), app.mail.smtp.host);
262         collectException(conf.mail.smtp.channel.value(), app.mail.smtp.channel);
263         collectException(conf.mail.smtp.port.as!ushort(), app.mail.smtp.port);
264         collectException(conf.mail.smtp.protocol.value(), app.mail.smtp.protocol);.
265         collectException(conf.mail.smtp.user.value(), app.mail.smtp.user);
266         collectException(conf.mail.smtp.password.value(), app.mail.smtp.password);
267 
268         collectException(conf.rpc.enabled.as!bool(), app.rpc.enabled);
269         collectException(conf.rpc.service.address.value(), app.rpc.service.address);
270         collectException(conf.rpc.service.port.as!ushort(), app.rpc.service.port);
271         collectException(conf.rpc.service.workerThreads.as!int(), app.rpc.service.workerThreads);.
272         collectException(conf.rpc.service.password.value(), app.rpc.service.password);
273 
274         collectException(conf.templates.path.value(), app.templates.path);
275 
276         return app;
277     }
278 
279 private:
280     Configuration _config;
281 
282     this()
283     {
284         http.workerThreads = totalCPUs;
285     }
286 }
287 
288 import core.sync.rwmutex;
289 
290 import std.file;
291 import std.path;
292 
293 class ConfigNotFoundException : Exception
294 {
295     mixin basicExceptionCtors;
296 }
297 
298 class ConfigManager
299 {
300     @property AppConfig app()
301     {
302         if(!_app)
303         {
304             setAppSection("");
305         }
306 
307         return _app;
308     }
309 
310     @property string path()
311     {
312         return this._path;
313     }
314 
315     void setConfigPath(string path)
316     {
317         if(path.empty)
318             return;
319 
320         if(path[$-1] == '/')
321             this._path = path;
322         else
323             this._path = path ~ "/";
324     }
325 
326     void setAppSection(string sec, string fileName = "application.conf")
327     {
328         string fullName = buildPath(path, fileName);
329         if(exists(fullName))
330         {
331             logDebugf("using config file: %s", fullName);
332             auto con = new Configuration(fullName, sec);
333             _app = AppConfig.parseAppConfig(con);
334         }
335         else
336         {
337             logDebug("using default configs.");
338             _app = new AppConfig();
339         }
340     }
341 
342     Configuration config(string key)
343     {
344         import std.format;
345         Configuration v = null;
346         synchronized(_mutex.reader)
347         {
348             v =  _conf.get(key, null);
349         }
350 
351         enforce!ConfigNotFoundException(v, format(" %s is not in config! ", key));
352 
353         return v;
354     }
355 
356     void addConfig(string key, Configuration conf)
357     {
358         _mutex.writer.lock();
359         scope(exit)_mutex.writer.unlock();
360         _conf[key] = conf;
361     }
362 
363     auto opDispatch(string s)()
364     {
365         return config(s);
366     }
367     
368 private:
369     this()
370     {
371         _mutex = new ReadWriteMutex();
372         _path = DEFAULT_CONFIG_PATH;
373     }
374     
375     ~this(){_mutex.destroy;}
376 
377     AppConfig _app;
378     
379     Configuration[string] _conf;
380 
381     string _path;
382 
383     ReadWriteMutex _mutex;
384 }
385 
386 @property ConfigManager Config()
387 {
388     return _manger;
389 }
390 
391 shared static this()
392 {
393     _manger = new ConfigManager();
394 }
395 
396 private:
397 __gshared ConfigManager _manger;